#include "fxcc/core/graph/opengl3/Shader.h"

using namespace fxcc::graph::opengl3;

Shader::Shader(const ShaderDesc &desc) : Program(), m_Desc(desc)
{
    Init();
};

fxcc::graph::opengl3::Shader::Shader(const std::string &vCode, const std::string &fCode)
    : Program()
{
    m_Desc.m_FragCode = fCode;
    m_Desc.m_VertCode = vCode;

    Init();
};

bool fxcc::graph::opengl3::Shader::Init()
{
    return Init1();
}


bool fxcc::graph::opengl3::Shader::Init1()
{
    bool flag{ true };
    FXCC_FAILED(flag = AttachCode(m_Desc.m_VertCode, GL_VERTEX_SHADER));
    FXCC_FAILED(flag = AttachCode(m_Desc.m_GeometryCode, GL_GEOMETRY_SHADER));

    FXCC_FAILED(flag = AttachCode(m_Desc.m_TessellationControlCode, GL_TESS_CONTROL_SHADER));
    FXCC_FAILED(flag = AttachCode(m_Desc.m_TessellationEvalutionCode, GL_TESS_EVALUATION_SHADER));
    FXCC_FAILED(flag = AttachCode(m_Desc.m_FragCode, GL_FRAGMENT_SHADER));

    FXCC_FAILED(flag = AttachCode(m_Desc.m_ComputeCode, GL_COMPUTE_SHADER));

    glLinkProgram(m_Id);
    return true;
}

bool fxcc::graph::opengl3::Shader::AttachCode(const std::string& code, int shaderType)
{
    std::string typeStr = ShaderFile::g_ShaderDescs[shaderType];

    if (!code.empty())
    {
        ShaderFile vFile;
        if (!vFile.CreateFrom(code.c_str(), shaderType))
        {
            ztclog::info("faield create shader %s", typeStr.c_str());
            return false;
        }
        glAttachShader(this->m_Id, vFile.m_Id);

    }
    return true;

}

void fxcc::graph::opengl3::Shader::DispatchCompute(int x, int y, int z)
{
    glDispatchCompute(x, y, z);
}

void fxcc::graph::opengl3::Shader::UseMaterial(const fxcc::graph::common::MaterialView& material)
{
    Use();
    UseMaterialParam(material.m_Params);
    UseTextureIndex(material.m_Textures);
    UseTextureMaps(material.m_Textures);

}

void fxcc::graph::opengl3::Shader::UseMaterialParam(const fxcc::graph::common::Params& params)
{
    for (const auto& it : params.m_Floats)
    {
        setFloat(it.first, it.second);
    }
    for (const auto& it : params.m_Vec2s)
    {
        setVec2(it.first, it.second);
    }
    for (const auto& it : params.m_Vec3s)
    {
        setVec3(it.first, it.second);
    }
    for (const auto& it : params.m_Vec4s)
    {
        setVec4(it.first, it.second);
    }

    for (const auto& it : params.m_Ints)
    {
        setInt(it.first, it.second);
    }
    for (const auto& it : params.m_IVec2s)
    {
        setIVec2(it.first, it.second);
    }
    for (const auto& it : params.m_IVec3s)
    {
        setIVec3(it.first, it.second);
    }
    for (const auto& it : params.m_IVec4s)
    {
        setIVec4(it.first, it.second);
    }
};

void fxcc::graph::opengl3::Shader::UseTextureIndex(const fxcc::graph::common::TextureMaps& textureMaps)
{

    int startIndex = textureMaps.m_GlslStartIndex;

    for (const auto& it : textureMaps.m_Texture2Ds)
    {
        auto key = it.first;
        auto textureMap = it.second;
        auto texture = textureMap.m_Value;

        if (texture)
        {
            setInt(key, startIndex);
            startIndex++;
        }
    }
    for (const auto& it : textureMaps.m_Texture2DArrays)
    {
        auto key = it.first;
        auto textureMap = it.second;
        auto texture = textureMap.m_Value;

        if (texture)
        {
            setInt(key, startIndex);
            startIndex++;
        }
    }
    for (const auto& it : textureMaps.m_TextureCubes)
    {
        auto key = it.first;
        auto textureMap = it.second;
        auto texture = textureMap.m_Value;

        if (texture)
        {
            setInt(key, startIndex);
            startIndex++;
        }
    }
    for (const auto& it : textureMaps.m_TextureCubeArrays)
    {
        auto key = it.first;
        auto textureMap = it.second;
        auto texture = textureMap.m_Value;

        if (texture)
        {
            setInt(key, startIndex);
            startIndex++;
        }
    }
    for (const auto& it : textureMaps.m_Texture3Ds)
    {
        auto key = it.first;
        auto textureMap = it.second;
        auto texture = textureMap.m_Value;

        if (texture)
        {
            setInt(key, startIndex);
            startIndex++;
        }
    }
}
;


void fxcc::graph::opengl3::Shader::UseTextureMaps(const fxcc::graph::common::TextureMaps& textureMaps)
{
    int startIndex = textureMaps.m_GlslStartIndex;

    for (const auto& it : textureMaps.m_Texture2Ds)
    {
        auto key = it.first;
        auto textureMap = it.second;
        auto texture = textureMap.m_Value;

        if (texture)
        {
            texture->SetShader(startIndex);
            startIndex++;
        }
    }
    for (const auto& it : textureMaps.m_Texture2DArrays)
    {
        auto key = it.first;
        auto textureMap = it.second;
        auto texture = textureMap.m_Value;

        if (texture)
        {
            texture->SetShader(startIndex);
            startIndex++;
        }
    }
    for (const auto& it : textureMaps.m_TextureCubes)
    {
        auto key = it.first;
        auto textureMap = it.second;
        auto texture = textureMap.m_Value;

        if (texture)
        {
            texture->SetShader(startIndex);
            startIndex++;
        }
    }
    for (const auto& it : textureMaps.m_TextureCubeArrays)
    {
        auto key = it.first;
        auto textureMap = it.second;
        auto texture = textureMap.m_Value;

        if (texture)
        {
            texture->SetShader(startIndex);
            startIndex++;
        }
    }
    for (const auto& it : textureMaps.m_Texture3Ds)
    {
        auto key = it.first;
        auto textureMap = it.second;
        auto texture = textureMap.m_Value;

        if (texture)
        {
            texture->SetShader(startIndex);
            startIndex++;
        }
    }
}
